Coverage Report

Created: 2024-12-26 12:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\api\core\loader.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::api::core::Error;
30
use crate::compiler::util::imports::ImportSolver;
31
use crate::{compiler, model};
32
use bp3d_debug::{error, trace};
33
use std::borrow::Cow;
34
use std::path::Path;
35
use crate::compiler::util::protocols::{Entry, ProtocolStore};
36
use crate::model::protocol::Import;
37
38
#[derive(Debug, Clone)]
39
pub struct Options<'a> {
40
    package: &'a str,
41
    exclude_from_generation: bool
42
}
43
44
impl Default for Options<'_> {
45
20
    fn default() -> Self {
46
20
        Self {
47
20
            package: "",
48
20
            exclude_from_generation: false
49
20
        }
50
20
    }
51
}
52
53
impl<'a> Options<'a> {
54
4
    pub fn from_package(package: &'a str) -> Self {
55
4
        Self {
56
4
            package,
57
4
            exclude_from_generation: false
58
4
        }
59
4
    }
60
61
1
    pub fn exclude_from_generation(&mut self) -> &mut Self {
62
1
        self.exclude_from_generation = true;
63
1
        self
64
1
    }
65
66
38
    pub fn is_excluded_from_generation(&self) -> bool {
67
38
        self.exclude_from_generation
68
38
    }
69
}
70
71
pub struct Loader<'a> {
72
    models: Vec<Entry<model::Protocol, Options<'a>>>,
73
    max_iterations: usize,
74
}
75
76
impl Default for Loader<'_> {
77
3
    fn default() -> Self {
78
3
        Self::new(16)
79
3
    }
80
}
81
82
impl<'a> Loader<'a> {
83
23
    pub fn new(max_iterations: usize) -> Self {
84
23
        Self {
85
23
            models: Vec::new(),
86
23
            max_iterations,
87
23
        }
88
23
    }
89
90
3
    pub fn load_from_folder(&mut self, path: impl AsRef<Path>, options: &Options<'a>) -> Result<(), Error> {
91
3
        trace!({path=?path.as_ref()} {?options}, "Loading folder");
92
46
        for a in 
std::fs::read_dir(path).map_err(Error::Io)3
?0
{
93
46
            let file = a.map_err(Error::Io)
?0
;
94
46
            if file.file_name().as_encoded_bytes().ends_with(b".json5") {
  Branch (94:16): [True: 20, False: 4]
  Branch (94:16): [Folded - Ignored]
  Branch (94:16): [True: 19, False: 3]
  Branch (94:16): [Folded - Ignored]
95
39
                self.load_from_file(file.path(), options)
?0
96
7
            }
97
        }
98
3
        Ok(())
99
3
    }
100
101
40
    pub fn load_from_file(&mut self, path: impl AsRef<Path>, options: &Options<'a>) -> Result<(), Error> {
102
40
        trace!({path=?path.as_ref()} {?options}, "Loading file");
103
40
        let content = std::fs::read_to_string(path).map_err(Error::Io)
?0
;
104
40
        self.load_from_string(content, options)
105
40
    }
106
107
60
    pub fn load_from_string(&mut self, content: impl AsRef<str>, options: &Options<'a>) -> Result<(), Error> {
108
60
        trace!({content=content.as_ref()} {?options}, "Loading string");
109
60
        let model: model::Protocol = json5::from_str(content.as_ref()).map_err(Error::Model)
?0
;
110
60
        if model.imports.as_ref().map(|v| 
v.len()11
).unwrap_or_default() > 0 {
  Branch (110:12): [True: 6, False: 15]
  Branch (110:12): [Folded - Ignored]
  Branch (110:12): [True: 5, False: 14]
  Branch (110:12): [Folded - Ignored]
  Branch (110:12): [True: 0, False: 5]
  Branch (110:12): [True: 0, False: 1]
  Branch (110:12): [True: 0, False: 1]
  Branch (110:12): [True: 0, False: 4]
  Branch (110:12): [True: 0, False: 3]
  Branch (110:12): [True: 0, False: 5]
  Branch (110:12): [True: 0, False: 1]
111
11
            self.models.insert(0, Entry { userdata: options.clone(), model });
112
49
        } else {
113
49
            self.models.push(Entry { userdata: options.clone(), model });
114
49
        }
115
60
        Ok(())
116
60
    }
117
118
2
    pub fn exclude(&mut self, name: &str) {
119
38
        self.models.retain(|entry| entry.model.name != name);
120
2
    }
121
122
23
    pub fn compile<T: ImportSolver>(mut self, solver: &T) -> Result<ProtocolStore<T, Options<'a>>, Error> {
123
23
        let mut protocols = ProtocolStore::new(solver);
124
23
        let mut iterations = self.max_iterations;
125
67
        while !self.models.is_empty() && 
iterations > 064
{
  Branch (125:15): [True: 23, False: 2]
  Branch (125:42): [True: 23, False: 0]
  Branch (125:15): [Folded - Ignored]
  Branch (125:42): [Folded - Ignored]
  Branch (125:15): [True: 21, False: 1]
  Branch (125:42): [True: 21, False: 0]
  Branch (125:15): [Folded - Ignored]
  Branch (125:42): [Folded - Ignored]
  Branch (125:15): [True: 5, False: 0]
  Branch (125:42): [True: 5, False: 0]
  Branch (125:15): [True: 1, False: 0]
  Branch (125:42): [True: 1, False: 0]
  Branch (125:15): [True: 1, False: 0]
  Branch (125:42): [True: 1, False: 0]
  Branch (125:15): [True: 4, False: 0]
  Branch (125:42): [True: 4, False: 0]
  Branch (125:15): [True: 3, False: 0]
  Branch (125:42): [True: 3, False: 0]
  Branch (125:15): [True: 5, False: 0]
  Branch (125:42): [True: 5, False: 0]
  Branch (125:15): [True: 1, False: 0]
  Branch (125:42): [True: 1, False: 0]
126
64
            let entry = self.models.pop().unwrap();
127
64
            trace!({imports=?entry.model.imports} {iterations=?iterations}, "Solving imports for model {}", entry.model.name);
128
64
            let check_not_exists = |package: &str, import: &Import| 
{45
129
45
                let full_name = if package.is_empty() || 
import.protocol.contains("::")1
{
  Branch (129:36): [True: 22, False: 1]
  Branch (129:58): [True: 1, False: 0]
  Branch (129:36): [Folded - Ignored]
  Branch (129:58): [Folded - Ignored]
  Branch (129:36): [True: 22, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [Folded - Ignored]
  Branch (129:58): [Folded - Ignored]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
  Branch (129:36): [True: 0, False: 0]
  Branch (129:58): [True: 0, False: 0]
130
45
                    Cow::Borrowed(&import.protocol)
131
                } else {
132
0
                    Cow::Owned(format!("{}::{}", package, import.protocol))
133
                };
134
45
                trace!("Searching for: {}", full_name);
135
45
                protocols.get(&full_name).is_none()
136
45
            };
137
64
            if entry.model
  Branch (137:16): [True: 3, False: 20]
  Branch (137:16): [Folded - Ignored]
  Branch (137:16): [True: 3, False: 18]
  Branch (137:16): [Folded - Ignored]
  Branch (137:16): [True: 0, False: 5]
  Branch (137:16): [True: 0, False: 1]
  Branch (137:16): [True: 0, False: 1]
  Branch (137:16): [True: 0, False: 4]
  Branch (137:16): [True: 0, False: 3]
  Branch (137:16): [True: 0, False: 5]
  Branch (137:16): [True: 0, False: 1]
138
64
                .imports
139
64
                .as_ref()
140
64
                .map(|v| 
v.iter().any(17
|v|
check_not_exists(entry.userdata.package, v)45
)17
)
141
64
                .unwrap_or_default()
142
            {
143
6
                self.models.insert(0, entry);
144
6
                iterations -= 1;
145
6
                continue;
146
58
            }
147
58
            let 
proto38
= compiler::Protocol::from_model(entry.model, &protocols, entry.userdata.package).map_err(Error::Compiler)
?20
;
148
38
            protocols.insert(Entry {
149
38
                model: proto,
150
38
                userdata: entry.userdata
151
38
            });
152
        }
153
3
        if iterations == 0 && 
!self.models.is_empty()0
{
  Branch (153:12): [True: 0, False: 2]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [Folded - Ignored]
  Branch (153:31): [Folded - Ignored]
  Branch (153:12): [True: 0, False: 1]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [Folded - Ignored]
  Branch (153:31): [Folded - Ignored]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
  Branch (153:12): [True: 0, False: 0]
  Branch (153:31): [True: 0, False: 0]
154
0
            error!(
155
0
                "Failed to solve protocol import order in {} iterations, {} model(s) could not be solved...",
156
0
                self.max_iterations,
157
0
                self.models.len()
158
0
            );
159
0
            return Err(Error::SolverMaxIterations);
160
3
        }
161
3
        Ok(protocols)
162
23
    }
163
}